package lion.framework.core.web;

import java.io.IOException;
import java.io.UnsupportedEncodingException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;

import lion.framework.core.conf.Config;
import lion.framework.core.conf.ConfigManager;
import lion.framework.core.db.SessionFactory;
import lion.framework.core.web.path.PathNode;
import lion.framework.core.web.path.PathNodeExecutor;
import lion.framework.core.web.path.PathNodeMapping;
import lion.framework.core.web.path.PathVar;
import lion.framework.core.web.request.RequestHandler;
import lion.framework.core.web.request.RequestMethod;
import lion.framework.core.web.views.ResultType;

/**
 * framework的过滤器,所有请求经过该过滤器处
 *
 * <pre>
 * time   2013 2013-4-24 上午11:31:50
 * mail   hl_0074@sina.com
 * </pre>
 *
 * @author lion
 */
@WebFilter(value = "/*", filterName = "lion", asyncSupported = true)
public class FilterX implements Filter {

	private Config config;
	private String sysencoding;
	private String serverMask;

	public FilterX() {

		config = ConfigManager.getConfig("framework");
		sysencoding = config.getString("framework.sysencoding", "UTF-8");
		serverMask = config.getString("framework.server-mask");
	}

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {

		filterConfig.getServletContext().setAttribute("pathvar", PathVar.getPathVars());
		filterConfig.getServletContext().setAttribute("webcontext", filterConfig.getServletContext().getContextPath());
		String serverName = config.getString("framework.servername");
		if (StringUtils.isNotBlank(serverName)) {
			filterConfig.getServletContext().setAttribute("servername", serverName);
		}

		// init the welcome-path
		String welcomePath = config.getString("framework.web.welcome-path");
		if (StringUtils.isNotBlank(welcomePath)) {
			PathNode root = PathNodeMapping.getRootPathNode();
			root.addView("success", welcomePath, ResultType.FORWARD);
			root.setMethodType(RequestMethod.ALL.name());
		}
	}

	@Override
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
			throws IOException, ServletException {

		//
		HttpServletRequest request = (HttpServletRequest) servletRequest;
		WrappedErrorResponse response = new WrappedErrorResponse(request, (HttpServletResponse) servletResponse);

		try {
			// some default
			prepare(request, response);

			String path = FilenameUtils
					.separatorsToUnix(request.getRequestURI().substring(request.getContextPath().length()));
			// handle request
			RequestHandler rh = new RequestHandler(path, chain);
			rh.handle(request, response);
		} catch (Throwable e) {
			PathNodeExecutor.onException(request, response, e);
		} finally {
			postFilter(request);
			RequestContext.unset();
			SessionContext.unset();
		}
	}

	/**
	 * post filter
	 *
	 * database connection release,upload temp file clean
	 */
	private void postFilter(HttpServletRequest req) {

		SessionFactory.cleanSession();
	}

	/**
	 * prepare for filter
	 */
	private void prepare(HttpServletRequest request, WrappedErrorResponse response)
			throws UnsupportedEncodingException {

		RequestContext.set(request, response);
		SessionContext.setSession(request.getSession());

		// ajax request,always utf8
		if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
			request.setCharacterEncoding("UTF-8");
		} else {
			request.setCharacterEncoding(sysencoding);
		}
		response.setCharacterEncoding(sysencoding);

		if (StringUtils.isNotBlank(serverMask)) {
			response.setHeader("Server", serverMask);
		}
	}

	@Override
	public void destroy() {

	}
}
